<?php
 class SmartMutex{
  var $filename;
  var $mutex_type;
  var $mutex_name;
  var $lock_acquired_time;
  var $lock_acquired;
  private $_file_handler;

  const SmartMutex_Domain   =0;
  const SmartMutex_Directory=1;
  const SmartMutex_Server   =2;// All virtual hosts on server with one tmp-folder e.g. "/tmp"

  function __construct($mutex_name, $mutex_type=null, $mutex_folder=null){
   $this->mutex_type  =($mutex_type  ==null) ? self::SmartMutex_Domain : $mutex_type;
   $this->mutex_folder=($mutex_folder==null) ? sys_get_temp_dir() : $mutex_folder;
   $this->mutex_name  =$mutex_name;
   $this->lock_acquired_time=0;
   $this->lock_acquired     =false;

   $prefix='';
   if ($this->mutex_type==self::SmartMutex_Domain){
    $prefix=md5(preg_replace('|^(www\\.)?([a-z0-9.-]+)(\\:[0-9])?|', '$2', strtolower($_SERVER['HTTP_HOST']))).'_';
   }
   if ($this->mutex_type==self::SmartMutex_Directory){
    $prefix=md5(strtolower($_SERVER['DOCUMENT_ROOT'])).'_';
   }
   $this->filename=$this->mutex_folder.DIRECTORY_SEPARATOR.'smartmutex_'.$prefix.$this->mutex_name.'.lock';
  }

  function __destruct(){
   $this->release_lock();
  }

  function is_free(){
   if ($this->lock_acquired){
    return false;
   }
   $fo    =fopen($this->filename, 'w');
   $result=flock($fo, LOCK_EX | LOCK_NB);
   flock($fo, LOCK_UN);
   fclose($fo);
   return $result;
  }

  function get_lock($time=-1){
   $tmp_time=microtime(true);

   $this->_file_handler=fopen($this->filename, 'w');
   if ($time>=0){
    $result =flock($this->_file_handler, LOCK_EX | LOCK_NB);

    while ((!$result)and($tmp_time+$time>=microtime(true))){
     // U MAD?
     usleep(10000);
     $result=flock($this->_file_handler, LOCK_EX | LOCK_NB);
    }
   }else{
    $result =flock($this->_file_handler, LOCK_EX);
   }

   if ($result){
    $this->lock_acquired_time=microtime(true);
    // @todo Не работает под Windows
    fwrite($this->_file_handler, posix_getpid()."\n".microtime(true)."\n".posix_getuid()."\n");
    fflush($this->_file_handler);
   }else{
    fclose($this->_file_handler);
   }

   $this->lock_acquired=$result;
   return $result;
  }

  function release_lock(){
   if ($this->lock_acquired){
    flock($this->_file_handler, LOCK_UN);
    fclose($this->_file_handler);
    @unlink($this->filename);
    $this->lock_acquired=false;
    $this->lock_acquired_time=0;
   }
  }


 }
?>